home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / jade / src / keys.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  16KB  |  624 lines

  1. /* keys.c -- Key binding and evaluating (this should be called events.c)
  2.    Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
  3.  
  4.    This file is part of Jade.
  5.  
  6.    Jade is free software; you can redistribute it and/or modify it
  7.    under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    Jade is distributed in the hope that it will be useful, but
  12.    WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with Jade; see the file COPYING.    If not, write to
  18.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "jade.h"
  21. #include "jade_protos.h"
  22.  
  23. #include <string.h>
  24. #include <stdlib.h>
  25.  
  26. /* Function to make a hash key from an event (c == code, m == modifiers)  */
  27. #define KEYTAB_HASH_FUN(c,m) (((c) * 33) + (((m) & EV_MOD_MASK)))
  28.  
  29. #define KEYTAB_SIZE 127
  30.  
  31. _PR VALUE usekey(void *, u_long, u_long, bool);
  32. _PR bool print_event_prefix(void);
  33. _PR void keys_init(void);
  34.  
  35. /* current_event holds the event we're processing (or 0s), last_event
  36.    contains the previously processed event.  */
  37. _PR u_long current_event[2], last_event[2];
  38. u_long current_event[2], last_event[2];
  39.  
  40. /* Pointer to the window system's representation of the current_event,
  41.    used for cooking events into strings.  */
  42. static void *current_os_event;
  43.  
  44. /* print_prefix means echo all events upto the end of the key-sequence.
  45.    printed_this_prefix says the last event has been echoed. */
  46. static bool print_prefix, printed_this_prefix;
  47.  
  48. /* Buffer holding the events making this key-sequence. */
  49. #define EVENT_BUFSIZ 20
  50. static u_long event_buf[EVENT_BUFSIZ]; /* one event = (code,mods) */
  51. static int event_index;
  52.  
  53. static VALUE sym_keymap_path, sym_unbound_key_hook, sym_esc_means_meta,
  54.          next_keymap_path, sym_keymap;
  55.  
  56. /* TRUE when the Meta qualifier should be added to the next event. */
  57. static bool pending_meta;
  58.  
  59. /* This doesn't belong here but I couldn't find anywhere else :-( */
  60. _PR VALUE sym_idle_hook;
  61. VALUE sym_idle_hook;
  62.  
  63. /* Some doc strings
  64. ::doc:keymap_path::
  65. A list of keymaps (ie, keylists and/or keytables). When an event occurs
  66. each keymap in the list is searched for an event binding which matches
  67. it. These bindings are installed in a keymap by the function `bind-keys'.
  68. See also `next-keymap-path'.
  69. ::end::
  70. ::doc:unbound_key_hook::
  71. When no event binding can be found for an event this hook is evaluated in
  72. the standard manner (see the function `eval-hook' for details).
  73. ::end::
  74. ::doc:esc_means_meta::
  75. When this variable is non-nil the `ESC' key means that the next event
  76. is qualified by the `Meta' modifier.
  77. This feature is included mainly for compatibility with GNU Emacs.
  78. ::end::
  79. ::doc:idle_hook::
  80. This hook gets evaluated every second while the editor is idle. Don't depend
  81. on how regularly this gets called, any events from the window-system will
  82. delay it. Also, auto-saving files and garbage-collection take precedence
  83. when there's idle time available. Use this hook sparingly, or for short
  84. periods only!
  85. ::end::
  86. */
  87.  
  88. /* Search the keymap KM for a binding of CODE&MODS.  */
  89. static VALUE
  90. findkey(VALUE km, u_long code, u_long mods)
  91. {
  92.     switch(VTYPE(km))
  93.     {
  94.     case V_Vector:
  95.     if(VVECT(km)->vc_Size != KEYTAB_SIZE)
  96.         return(NULL);
  97.     km = VVECT(km)->vc_Array[KEYTAB_HASH_FUN(code, mods) % KEYTAB_SIZE];
  98.     break;
  99.     case V_Cons:
  100.     km = VCDR(km);
  101.     break;
  102.     }
  103.     while(CONSP(km))
  104.     {
  105.     VALUE this = VCAR(km);
  106.     if((VNUM(VVECTI(this, KEY_MODS)) == mods)
  107.        && (VNUM(VVECTI(this, KEY_CODE)) == code))
  108.         return(this);
  109.     km = VCDR(km);
  110.     }
  111.     return(NULL);
  112. }
  113.  
  114. /* Search for a binding of CODE&MODS.  */
  115. static VALUE
  116. lookup_binding(u_long code, u_long mods)
  117. {
  118.     VALUE kp;
  119.     if(!NILP(next_keymap_path))
  120.     {
  121.     kp = next_keymap_path;
  122.     next_keymap_path = sym_nil;
  123.     }
  124.     else
  125.     {
  126.     kp = cmd_symbol_value(sym_keymap_path, sym_t);
  127.     if(VOIDP(kp))
  128.         return(NULL);
  129.     }
  130.     while(CONSP(kp))
  131.     {
  132.     VALUE thispath = VCAR(kp);
  133.     VALUE k;
  134.     if(SYMBOLP(thispath))
  135.         thispath = cmd_symbol_value(thispath, sym_t);
  136.     if(!VOIDP(thispath))
  137.     {
  138.         if(!NILP(cmd_keymapp(thispath)))
  139.         {
  140.         k = findkey(thispath, code, mods);
  141.         if(k && VECTORP(k))
  142.             return(VVECTI(k, KEY_COMMAND));
  143.         }
  144.     }
  145.     kp = VCDR(kp);
  146.     }
  147.     return(NULL);
  148. }
  149.  
  150. /* Process the event CODE+MODS, CURS-STATE is TRUE if the cursor is drawn.
  151.    OS-INPUT-MSG is the raw input event from the window-system, this is
  152.    only used to cook a string from.  */
  153. VALUE
  154. usekey(void *OSInputMsg, u_long code, u_long mods, bool cursState)
  155. {
  156.     VALUE result = sym_nil;
  157.     event_buf[event_index++] = code;
  158.     event_buf[event_index++] = mods;
  159.     if(event_index == EVENT_BUFSIZ)
  160.     event_index = 0;
  161.     printed_this_prefix = FALSE;
  162.     if(!NILP(VSYM(sym_esc_means_meta)->sym_Value)
  163.        && !pending_meta
  164.        && (code == esc_code) && (mods == esc_mods))
  165.     {
  166.     /* Treat this ESC as a Meta-prefix. */
  167.     pending_meta = TRUE;
  168.     }
  169.     else
  170.     {
  171.     VALUE cmd;
  172.     VW *vw = curr_vw;
  173.     bool inmulti = !(NILP(next_keymap_path)
  174.              || (next_keymap_path == sym_t));
  175.     if(pending_meta)
  176.     {
  177.         mods |= EV_MOD_META;
  178.         pending_meta = FALSE;
  179.     }
  180.     current_event[0] = code;
  181.     current_event[1] = mods;
  182.     current_os_event = OSInputMsg;
  183.     reset_message(vw);
  184.     cmd = lookup_binding(code, mods);
  185.     if(cmd)
  186.     {
  187.         if(cursState)
  188.         {
  189.         cursor(vw, CURS_OFF);
  190.         cursState = FALSE;
  191.         }
  192.         result = cmd_call_command(cmd, sym_nil);
  193.     }
  194.     else if(inmulti)
  195.         beep(vw);
  196.     else
  197.     {
  198.         if(cursState)
  199.         {
  200.         cursor(vw, CURS_OFF);
  201.         cursState = FALSE;
  202.         }
  203.         result = cmd_eval_hook2(sym_unbound_key_hook, sym_nil);
  204.         if(result && !NILP(result))
  205.         undo_distinct();
  206.         else if(result && (mods & EV_TYPE_KEYBD) && OSInputMsg)
  207.         {
  208.         u_char buff[256];
  209.         int len;
  210.         if((len = cook_key(OSInputMsg, buff, 256 - 1)) >= 0)
  211.         {
  212.             buff[len] = 0;
  213.             if(len > 0)
  214.             {
  215.             if(!read_only(vw->vw_Tx))
  216.             {
  217.                 POS tmp = vw->vw_CursorPos;
  218.                 if(last_command != sym_t)
  219.                 undo_new_group();
  220.                 if(pad_cursor(vw))
  221.                 insert_string(vw->vw_Tx, buff, len, &tmp);
  222.                 last_command = sym_t;
  223.                 result = sym_t;
  224.             }
  225.             else
  226.                 result = NULL;
  227.             }
  228.         }
  229.         else
  230.             message("error: key translation screwup");
  231.         }
  232.     }
  233.     last_event[0] = current_event[0];
  234.     last_event[1] = current_event[1];
  235.     current_event[0] = current_event[1] = 0;
  236.     current_os_event = NULL;
  237.     }
  238.     if(curr_vw)
  239.     {
  240.     if(print_prefix)
  241.     {
  242.         print_event_prefix();
  243.         if(NILP(next_keymap_path)
  244.            && !pending_meta)
  245.         {
  246.         print_prefix = FALSE;
  247.         }
  248.     }
  249.     std_message(curr_vw);
  250.     refresh_world();
  251.     if(!cursState)
  252.         cursor(curr_vw, CURS_ON);
  253.     }
  254.     if(NILP(next_keymap_path) && !pending_meta)
  255.     event_index = 0;
  256.     return(result);
  257. }
  258.  
  259. _PR VALUE cmd_make_keytab(void);
  260. DEFUN("make-keytab", cmd_make_keytab, subr_make_keytab, (void), V_Subr0, DOC_make_keytab) /*
  261. ::doc:make_keytab::
  262. make-keytab
  263.  
  264. Return a new key-table suitable for storing bindings in. This is a 127
  265. element vector, each element is an empty list of bindings.
  266. ::end:: */
  267. {
  268.     return(cmd_make_vector(make_number(KEYTAB_SIZE), sym_nil));
  269. }
  270.  
  271. _PR VALUE cmd_make_keylist(void);
  272. DEFUN("make-keylist", cmd_make_keylist, subr_make_keylist, (void), V_Subr0, DOC_make_keylist) /*
  273. ::doc:make_keylist::
  274. make-keylist
  275.  
  276. Return a new key-list suitable for storing bindings in. This is a cons
  277. cell looking like `(keymap . LIST-OF-BINDINGS)', LIST-OF-BINDINGS starts
  278. off empty.
  279. ::end:: */
  280. {
  281.     return(cmd_cons(sym_keymap, sym_nil));
  282. }
  283.  
  284. _PR VALUE cmd_bind_keys(VALUE args);
  285. DEFUN("bind-keys", cmd_bind_keys, subr_bind_keys, (VALUE args), V_SubrN, DOC_bind_keys) /*
  286. ::doc:bind_keys::
  287. bind-keys KEY-MAP { EVENT-DESCRIPTION COMMAND }...
  288. ::end:: */
  289. {
  290.     bool rc = TRUE;
  291.     VALUE km, arg1, res = NULL;
  292.     if(!CONSP(args))
  293.     return(NULL);
  294.     km = VCAR(args);
  295.     args = VCDR(args);
  296.     while(rc && CONSP(args) && CONSP(VCDR(args)))
  297.     {
  298.     u_long code, mods;
  299.     VALUE key;
  300.     arg1 = VCAR(args);
  301.     args = VCDR(args);
  302.     if(STRINGP(arg1))
  303.     {
  304.         if(!lookup_event(&code, &mods, VSTR(arg1)))
  305.         goto end;
  306.     }
  307.     else if(!NILP(cmd_eventp(arg1)))
  308.     {
  309.         code = VNUM(VCAR(arg1));
  310.         mods = VNUM(VCDR(arg1));
  311.     }
  312.     else
  313.     {
  314.         cmd_signal(sym_bad_event_desc, LIST_1(arg1));
  315.         goto end;
  316.     }
  317.     rc = FALSE;
  318.     key = make_vector(3);
  319.     if(key)
  320.     {
  321.         VVECTI(key, KEY_CODE) = make_number(code);
  322.         VVECTI(key, KEY_MODS) = make_number(mods);
  323.         VVECTI(key, KEY_COMMAND) = VCAR(args);
  324.         if(VECTORP(km))
  325.         {
  326.         u_long hash = KEYTAB_HASH_FUN(code, mods) % KEYTAB_SIZE;
  327.         VALUE old = VVECTI(km, hash);
  328.         VVECTI(km, hash) = cmd_cons(key, old);
  329.         }
  330.         else
  331.         VCDR(km) = cmd_cons(key, VCDR(km));
  332.         args = VCDR(args);
  333.         rc = TRUE;
  334.     }
  335.     else
  336.         goto end;
  337.     }
  338.     if(rc)
  339.     res = sym_t;
  340. end:
  341.     return(res);
  342. }
  343.  
  344. _PR VALUE cmd_unbind_keys(VALUE args);
  345. DEFUN("unbind-keys", cmd_unbind_keys, subr_unbind_keys, (VALUE args), V_SubrN, DOC_unbind_keys) /*
  346. ::doc:unbind_keys::
  347. unbind-keys KEY-MAP EVENT-DESCRIPTION...
  348. ::end:: */
  349. {
  350.     bool rc = TRUE;
  351.     VALUE km, arg1, res = NULL;
  352.     if(!CONSP(args))
  353.     return(NULL);
  354.     km = VCAR(args);
  355.     if(!((VECTORP(km) && VVECT(km)->vc_Size == KEYTAB_SIZE)
  356.        || CONSP(km)))
  357.     return(signal_arg_error(km, 1));
  358.     args = VCDR(args);
  359.     while(rc && CONSP(args))
  360.     {
  361.     u_long code, mods;
  362.     VALUE *keyp;
  363.     arg1 = VCAR(args);
  364.     if(STRINGP(arg1))
  365.     {
  366.         if(!lookup_event(&code, &mods, VSTR(arg1)))
  367.         goto end;
  368.     }
  369.     else if(!NILP(cmd_eventp(arg1)))
  370.     {
  371.         code = VNUM(VCAR(arg1));
  372.         mods = VNUM(VCDR(arg1));
  373.     }
  374.     else
  375.     {
  376.         cmd_signal(sym_bad_event_desc, LIST_1(arg1));
  377.         goto end;
  378.     }
  379.     rc = FALSE;
  380.     if(VECTORP(km))
  381.         keyp = &VVECTI(km, KEYTAB_HASH_FUN(code, mods) % KEYTAB_SIZE);
  382.     else
  383.         keyp = &VCDR(km);
  384.     while(CONSP(*keyp))
  385.     {
  386.         /* This code is borrowed from cmd_delq */
  387.         if((VNUM(VVECTI(VCAR(*keyp), KEY_MODS)) == mods)
  388.            && (VNUM(VVECTI(VCAR(*keyp), KEY_CODE)) == code))
  389.         {
  390.         *keyp = VCDR(*keyp);
  391.         /* Keybindings are supposed to nest so only delete the
  392.            first entry for this event  */
  393.         break;
  394.         }
  395.         else
  396.         keyp = &VCDR(*keyp);
  397.         TEST_INT;
  398.         if(INT_P)
  399.         return(NULL);
  400.     }
  401.     rc = TRUE;
  402.     args = VCDR(args);
  403.     }
  404.     if(rc)
  405.     res = sym_t;
  406. end:
  407.     return(res);
  408. }
  409.  
  410. _PR VALUE var_next_keymap_path(VALUE val);
  411. DEFUN("next-keymap-path", var_next_keymap_path, subr_next_keymap_path, (VALUE val), V_Var, DOC_next_keymap_path) /*
  412. ::doc:next_keymap_path::
  413. The value of `keymap-path' to be used for the *next* keypress. This is
  414. usually used to chain together multi-key bindings.
  415. ::end:: */
  416. {
  417.     if(val)
  418.     next_keymap_path = val;
  419.     /* This isn't a true command */
  420.     this_command = sym_nil;
  421.     /* Pass the prefix-arg along */
  422.     var_prefix_arg(var_current_prefix_arg(NULL));
  423.     return(next_keymap_path);
  424. }
  425.  
  426. _PR VALUE cmd_current_event_string(void);
  427. DEFUN("current-event-string", cmd_current_event_string, subr_current_event_string, (void), V_Subr0, DOC_current_event_string) /*
  428. ::doc:current_event_string::
  429. current-event-string
  430.  
  431. Returns the string which would have been inserted by the current event if
  432. a Lisp function hadn't been called instead.
  433. ::end:: */
  434. {
  435.     u_char buff[256];
  436.     int len;
  437.     if(!current_os_event)
  438.     return(cmd_signal(sym_error, LIST_1(MKSTR("Not in event handler"))));
  439.     len = cook_key(current_os_event, buff, 256 - 1);
  440.     if(len > 0)
  441.     return(string_dupn(buff, len));
  442.     return(null_string);
  443. }
  444.  
  445. _PR VALUE cmd_current_event(void);
  446. DEFUN("current-event", cmd_current_event, subr_current_event, (void), V_Subr0, DOC_current_event) /*
  447. ::doc:current_event::
  448. current-event
  449.  
  450. Return the event which caused the current command to be invoked.
  451. ::end:: */
  452. {
  453.     if(current_event[1])
  454.     return(cmd_cons(make_number(current_event[0]),
  455.             make_number(current_event[1])));
  456.     else
  457.     return(sym_nil);
  458. }
  459.  
  460. _PR VALUE cmd_last_event(void);
  461. DEFUN("last-event", cmd_last_event, subr_last_event, (void), V_Subr0, DOC_last_event) /*
  462. ::doc:last_event::
  463. last-event
  464.  
  465. Return the previous event which occurred.
  466. ::end:: */
  467. {
  468.     if(last_event[1])
  469.     return(cmd_cons(make_number(last_event[0]),
  470.             make_number(last_event[1])));
  471.     else
  472.     return(sym_nil);
  473. }
  474.  
  475. _PR VALUE cmd_event_name(VALUE ev);
  476. DEFUN("event-name", cmd_event_name, subr_event_name, (VALUE ev), V_Subr1, DOC_event_name) /*
  477. ::doc:event_name::
  478. event-name EVENT
  479.  
  480. Returns a string naming the event EVENT.
  481. ::end:: */
  482. {
  483.     u_char buf[256];
  484.     if(NILP(cmd_eventp(ev)))
  485.     return(signal_arg_error(ev, 1));
  486.     if(lookup_event_name(buf, VNUM(VCAR(ev)), VNUM(VCDR(ev))))
  487.     return(string_dup(buf));
  488.     return(sym_nil);
  489. }
  490.  
  491. _PR VALUE cmd_lookup_event(VALUE name);
  492. DEFUN("lookup-event", cmd_lookup_event, subr_lookup_event, (VALUE name), V_Subr1, DOC_lookup_event) /*
  493. ::doc:lookup_event::
  494. lookup-event EVENT-NAME
  495.  
  496. Return the event whose name is EVENT-NAME.
  497. ::end:: */
  498. {
  499.     u_long code, mods;
  500.     DECLARE1(name, STRINGP);
  501.     if(lookup_event(&code, &mods, VSTR(name)))
  502.     return(cmd_cons(make_number(code), make_number(mods)));
  503.     else
  504.     return(sym_nil);
  505. }
  506.  
  507. _PR VALUE cmd_lookup_event_binding(VALUE ev, VALUE reset);
  508. DEFUN("lookup-event-binding", cmd_lookup_event_binding, subr_lookup_event_binding, (VALUE ev, VALUE reset), V_Subr2, DOC_lookup_event_binding) /*
  509. ::doc:lookup_event_binding::
  510. lookup-event-binding EVENT [RESET-PATH]
  511.  
  512. Return the command currently associated with the event EVENT.
  513. If RESET-PATH is non-nil the value of `next-keymap-path' will be cleared
  514. after being used.
  515. ::end:: */
  516. {
  517.     VALUE old_next_km_path = next_keymap_path;
  518.     VALUE res;
  519.     if(NILP(cmd_eventp(ev)))
  520.     return(signal_arg_error(ev, 1));
  521.     res = lookup_binding(VNUM(VCAR(ev)), VNUM(VCDR(ev)));
  522.     if(NILP(reset))
  523.     {
  524.     /* We don't want next-keymap-path to be reset */
  525.     next_keymap_path = old_next_km_path;
  526.     }
  527.     return(res ? res : sym_nil);
  528. }
  529.  
  530. _PR VALUE cmd_keymapp(VALUE arg);
  531. DEFUN("keymapp", cmd_keymapp, subr_keymapp, (VALUE arg), V_Subr1, DOC_keymapp) /*
  532. ::doc:keymapp::
  533. keymapp ARG
  534.  
  535. Returns t if ARG can be used as a keymap.
  536. ::end:: */
  537. {
  538.     if((VECTORP(arg) && VVECT(arg)->vc_Size == KEYTAB_SIZE)
  539.        || (CONSP(arg) && VCAR(arg) == sym_keymap))
  540.     return(sym_t);
  541.     return(sym_nil);
  542. }
  543.  
  544. _PR VALUE cmd_eventp(VALUE arg);
  545. DEFUN("eventp", cmd_eventp, subr_eventp, (VALUE arg), V_Subr1, DOC_eventp) /*
  546. ::doc:eventp::
  547. eventp ARG
  548.  
  549. Returns t if the ARG is an input event.
  550. ::end:: */
  551. {
  552.     if(CONSP(arg) && NUMBERP(VCAR(arg)) && NUMBERP(VCDR(arg)))
  553.     return(sym_t);
  554.     else
  555.     return(sym_nil);
  556. }
  557.  
  558. /* If necessary, print the name of the current event prefix and return
  559.    TRUE, else return FALSE.  */
  560. bool
  561. print_event_prefix(void)
  562. {
  563.     int i;
  564.     u_char buf[256];
  565.     u_char *bufp = buf;
  566.     if((NILP(next_keymap_path) && !pending_meta)
  567.        && (!print_prefix || printed_this_prefix))
  568.     {
  569.     print_prefix = FALSE;
  570.     return(FALSE);
  571.     }
  572.     if(!print_prefix)
  573.     print_prefix = TRUE;
  574.     for(i = 0; i < event_index; i += 2)
  575.     {
  576.     if(lookup_event_name(bufp, event_buf[i], event_buf[i+1]))
  577.     {
  578.         bufp += strlen(bufp);
  579.         *bufp++ = ' ';
  580.     }
  581.     }
  582.     if(!NILP(next_keymap_path) || pending_meta)
  583.     {
  584.     if(bufp > buf)
  585.         bufp--;            /* erase the last space */
  586.     *bufp++ = '.';
  587.     *bufp++ = '.';
  588.     *bufp++ = '.';
  589.     }
  590.     messagen(buf, bufp - buf);
  591.     printed_this_prefix = TRUE;
  592.     return(TRUE);
  593. }
  594.  
  595. void
  596. keys_init(void)
  597. {
  598.     INTERN(sym_keymap_path, "keymap-path");
  599.     DOC_VAR(sym_keymap_path, DOC_keymap_path);
  600.     INTERN(sym_unbound_key_hook, "unbound-key-hook");
  601.     DOC_VAR(sym_unbound_key_hook, DOC_unbound_key_hook);
  602.     INTERN(sym_esc_means_meta, "esc-means-meta");
  603.     VSYM(sym_esc_means_meta)->sym_Value = sym_t;
  604.     DOC_VAR(sym_esc_means_meta, DOC_esc_means_meta);
  605.     INTERN(sym_idle_hook, "idle-hook");
  606.     DOC_VAR(sym_idle_hook, DOC_idle_hook);
  607.     INTERN(sym_keymap, "keymap");
  608.     next_keymap_path = sym_nil;
  609.     mark_static(&next_keymap_path);
  610.     ADD_SUBR(subr_make_keytab);
  611.     ADD_SUBR(subr_make_keylist);
  612.     ADD_SUBR(subr_bind_keys);
  613.     ADD_SUBR(subr_unbind_keys);
  614.     ADD_SUBR(subr_next_keymap_path);
  615.     ADD_SUBR(subr_current_event_string);
  616.     ADD_SUBR(subr_current_event);
  617.     ADD_SUBR(subr_last_event);
  618.     ADD_SUBR(subr_event_name);
  619.     ADD_SUBR(subr_lookup_event);
  620.     ADD_SUBR(subr_lookup_event_binding);
  621.     ADD_SUBR(subr_keymapp);
  622.     ADD_SUBR(subr_eventp);
  623. }
  624.